#include <PackageCatcher.h>
#include <time.h>
#include <functional>

namespace wzp {
    
    static void packetHandler(unsigned char* param, const struct pcap_pkthdr* header, const unsigned char* pkt_data)
    {
        struct tm* ltime;
        char timestr[16];
        ip_header* ih;
        udp_header* uh;
        u_int ip_len;
        u_short sport, dport;
        time_t local_tv_sec;

        /*
         * unused parameter
         */
        (VOID)(param);

        /* convert the timestamp to readable format */
        local_tv_sec = header->ts.tv_sec;
        ltime = localtime(&local_tv_sec);
        strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

        /* print timestamp and length of the packet */
        printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);

        /* retireve the position of the ip header */
        ih = (ip_header*)(pkt_data +
            14); //length of ethernet header

        /* retireve the position of the udp header */
        ip_len = (ih->ver_ihl & 0xf) * 4;
        uh = (udp_header*)((u_char*)ih + ip_len);

        /* convert from network byte order to host byte order */
        sport = ntohs(uh->sport);
        dport = ntohs(uh->dport);

        /* print ip addresses and udp ports */
        printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
            ih->saddr.byte1,
            ih->saddr.byte2,
            ih->saddr.byte3,
            ih->saddr.byte4,
            sport,
            ih->daddr.byte1,
            ih->daddr.byte2,
            ih->daddr.byte3,
            ih->daddr.byte4,
            dport);
    }

    PackageCatcher::~PackageCatcher()
    {
        if (ifaces_) {
            /* Free the device list */
            pcap_freealldevs(ifaces_);
        }
        if (adhandle) {
            pcap_close(adhandle);
        }
    }
	void PackageCatcher::initNpcap()
	{
        if (0 != pcap_init(PCAP_CHAR_ENC_LOCAL, errbuf_)) {
            fprintf(stderr, "Failed to initialize pcap lib: %s\n", errbuf_);
            printf("%s", stderr);
        }
        if (0 != pcap_findalldevs(&ifaces_, errbuf_)) {
            fprintf(stderr, "Failed to get list of devices: %s\n", errbuf_);
            printf("%s", stderr);
        }
        printf("initPcap ok! \n");
        int i = 0;
        for (pcap_if_t * d = ifaces_; d; d = d->next)
        {
            DeviceInfo di;
            di.name_ = d->name;
            di.description_ = d->description;
            printf("%d : %s -> %s \n", i, d->name, d->description);
            devList_.push_back(di);
            i++;
        }
        state_ = wzp::CatcherState::init;
	}

    /* Callback function invoked by libpcap for every incoming packet */
    //void PackageCatcher::packetHandler(unsigned char* param, const struct pcap_pkthdr* header, const unsigned char* pkt_data)
    //{
    //    
    //}
    void PackageCatcher::initNpcapDevice(int devIndex, const std::string& filter)
    {
        int i = 0;
        pcap_if_t* selectDev = NULL;
        unsigned int netmask;
        struct bpf_program fcode;
        char packet_filter[] = "ip and udp";

        // Select device
        for (selectDev = ifaces_; i < devIndex; selectDev = selectDev->next, i++);

        if ((adhandle = pcap_open_live(selectDev->name,	// name of the device
            65536,			// portion of the packet to capture. 
                           // 65536 grants that the whole packet will be captured on all the MACs.
            1,				// promiscuous mode (nonzero means promiscuous)
            1000,			// read timeout
            errbuf_			// error buffer
        )) == NULL)
        {
            fprintf(stderr, "\nUnable to open the adapter. %s is not supported by Npcap\n", selectDev->name);
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            ifaces_ = NULL;
        }

        /* Check the link layer. We support only Ethernet for simplicity. */
        if (pcap_datalink(adhandle) != DLT_EN10MB)
        {
            fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            ifaces_ = NULL;
        }

        if (selectDev->addresses != NULL)
            /* Retrieve the mask of the first address of the interface */
            netmask = ((struct sockaddr_in*)(selectDev->addresses->netmask))->sin_addr.S_un.S_addr;
        else
            /* If the interface is without addresses we suppose to be in a C class network */
            netmask = 0xffffff;

        //compile the filter
        if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
        {
            fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            ifaces_ = NULL;
        }

        //set the filter
        if (pcap_setfilter(adhandle, &fcode) < 0)
        {
            fprintf(stderr, "\nError setting the filter.\n");
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            ifaces_ = NULL;
        }

        printf("\nlistening on %s...\n", selectDev->description);

        /* At this point, we don't need any more the device list. Free it */
        //pcap_freealldevs(ifaces_);
        state_ = wzp::CatcherState::initDev;
    }
    void PackageCatcher::endNpcapLoop()
    {
        pcap_breakloop(adhandle);
        if (adhandle) {
            pcap_close(adhandle);
        }
        state_ = wzp::CatcherState::end;
        printf("endNpcapLoop adhandle = %p\n", adhandle);
    }
    void PackageCatcher::startNpcapLoop()
    {
        /* start the capture */
        //packetCallBack callBack = std::bind(&PackageCatcher::packetHandler, this, _1, _2, _3);
        state_ = wzp::CatcherState::loop;
        pcap_loop(adhandle, 0, wzp::packetHandler, NULL);
        printf("pcap_loop end!\n");
    }
    int PackageCatcher::startCapture(int devIndex)
    {
        int i = 0;
        pcap_if_t* selectDev = NULL;
        unsigned int netmask;
        struct bpf_program fcode;
        char packet_filter[] = "ip and udp";

        // Select device
        for (selectDev = ifaces_; i < devIndex; selectDev = selectDev->next, i++);

        if ((adhandle = pcap_open_live(selectDev->name,	// name of the device
            65536,			// portion of the packet to capture. 
                           // 65536 grants that the whole packet will be captured on all the MACs.
            1,				// promiscuous mode (nonzero means promiscuous)
            1000,			// read timeout
            errbuf_			// error buffer
        )) == NULL)
        {
            fprintf(stderr, "\nUnable to open the adapter. %s is not supported by Npcap\n", selectDev->name);
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            return -1;
        }
       
        /* Check the link layer. We support only Ethernet for simplicity. */
        if (pcap_datalink(adhandle) != DLT_EN10MB)
        {
            fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            return -1;
        }

        if (selectDev->addresses != NULL)
            /* Retrieve the mask of the first address of the interface */
            netmask = ((struct sockaddr_in*)(selectDev->addresses->netmask))->sin_addr.S_un.S_addr;
        else
            /* If the interface is without addresses we suppose to be in a C class network */
            netmask = 0xffffff;

        //compile the filter
        if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
        {
            fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            return -1;
        }

        //set the filter
        if (pcap_setfilter(adhandle, &fcode) < 0)
        {
            fprintf(stderr, "\nError setting the filter.\n");
            /* Free the device list */
            pcap_freealldevs(ifaces_);
            return -1;
        }

        printf("\nlistening on %s...\n", selectDev->description);

        /* At this point, we don't need any more the device list. Free it */
        pcap_freealldevs(ifaces_);

        /* start the capture */
        //packetCallBack callBack = std::bind(&PackageCatcher::packetHandler, this, _1, _2, _3);
        pcap_loop(adhandle, 0, wzp::packetHandler, NULL);
    }
}

